/*
 * GradientedSurface.cpp
 *
 *  Created on: 07/mar/2013
 *      Author: lordmzn
 */

#include "MOLEM/DEMSurface.h"
#include <list>
#include <algorithm>

namespace MOLEM {

DEMSurface::DEMSurface(unsigned int nrp, unsigned int ncp) :
		state(VALUES_NOT_SET), nr(nrp), nc(ncp), datas(nr * nc), sumZvalues(
				SUM_Z_DEFAULT_VALUE) {
	initElements();
	connectElements();
}

DEMSurface::~DEMSurface() {
	for (std::vector<DEMElement*>::iterator it = datas.begin();
			it != datas.end(); ++it) {
		delete *it;
	}
}

void DEMSurface::initElements() {
	std::vector<DEMElement*>::iterator it;
	// create els row by row and store them sequentially in datas
	unsigned int row = 0;
	unsigned int col = 1;
	*datas.begin() = new DEMElement(0, 0, Direction::NORTH_WEST);
	for (it = ++(datas.begin()); it != datas.end(); ++it) {
		if (row == 0) {
			if (col == nc - 1) {
				(*it) = new DEMElement(row, col, Direction::NORTH_EAST);
			} else {
				(*it) = new DEMElement(row, col, Direction::NORTH);
			}
		} else if (col == 0) { // left one = west
			if (row == nr - 1) {
				(*it) = new DEMElement(row, col, Direction::SOUTH_WEST);
			} else {
				(*it) = new DEMElement(row, col, Direction::WEST);
			}
		} else if (col == nc - 1) { // right one = east
			if (row == nr - 1) {
				(*it) = new DEMElement(row, col, Direction::SOUTH_EAST);
			} else {
				(*it) = new DEMElement(row, col, Direction::EAST);
			}
		} else if (row == nr - 1) { // lower one = south
			(*it) = new DEMElement(row, col, Direction::SOUTH);
		} else {
			// it's not on the boundary
			(*it) = new DEMElement(row, col);
		}
		col++;
		if (col == nc) {
			row++;
			col = 0;
		}
	}
}

void DEMSurface::connectElements() {
	std::vector<DEMElement*>::iterator it;
	// connect elements
	DEMElement* west;
	DEMElement* north;
	DEMElement* east;
	DEMElement* south;
	it = datas.begin();
	for (unsigned int row = 0; row < nr; row++) {
		for (unsigned int col = 0; col < nc; col++) {
			if (col == 0) {
				west = *it;
			} else {
				west = *(it - 1);
			}
			if (row == 0) {
				north = *it;
			} else {
				north = *(it - nc);
			}
			if (col == nc - 1) {
				east = *it;
			} else {
				east = *(it + 1);
			}
			if (row == nr - 1) {
				south = *it;
			} else {
				south = *(it + nc);
			}
			(*it)->connect(*west, *north, *east, *south);
		}
	}
}

SurfStatus DEMSurface::setValues(std::vector<double>& values) {
	// check dimension
	if (values.size() != nr * nc) {
		return VALUES_NOT_SET;
	}
	// set element' z values
	std::vector<DEMElement* >::iterator data_it = datas.begin();
	for (std::vector<double>::iterator vals_it = values.begin();
			vals_it != values.end(); ++vals_it) {
		(*data_it)->setZvalue(*vals_it);
		++data_it;
	}
	sumZvalues = SUM_Z_DEFAULT_VALUE;
	state = VALUES_CORRECTLY_SET;
	return VALUES_CORRECTLY_SET;
}

double DEMSurface::getSumZvalues() {
	if (sumZvalues == SUM_Z_DEFAULT_VALUE) {
		sumZvalues = 0.0;
		for (std::vector<DEMElement*>::iterator it = datas.begin();
				it != datas.end(); ++it) {
			sumZvalues += (*it)->getZvalue();
		}
	}
	return sumZvalues;
}


SurfStatus DEMSurface::initGradientField() {
	if (state != VALUES_CORRECTLY_SET) {
		return GRADIENT_FIELD_UNINITIALIZED;
	}
	// search for the springs
	std::list<DEMElement*> datasOrderedByZ(nr * nc, 0x0);
	std::copy(datas.begin(), datas.end(), datasOrderedByZ.begin());
	datasOrderedByZ.sort();
	// start from the highest element. mark it as a spring and call the find direction
	// check each following element (in the ordered list) to see if its direction has
	// been evaluated. if its not, mark it as spring and call find direction
	for (std::list<DEMElement*>::iterator it_ordered = datasOrderedByZ.begin();
			it_ordered != datasOrderedByZ.end(); ++it_ordered) {
		if ((*it_ordered)->isDirectionEvaluated()) {
			continue;
		} else {
			(*it_ordered)->markAsSpring();
			(*it_ordered)->findDirection();
		}
	}
	// perform DF
	depressionFilling() == DEPRESSION_FILLING_PERFORMED ?
			state = GRADIENT_FIELD_INITIALIZED : state =
					GRADIENT_FIELD_UNINITIALIZED;
	return state;
}

SurfStatus DEMSurface::depressionFilling() {
	return DEPRESSION_FILLING_PERFORMED;
}

} /* namespace MOLEM */
